package com.lagou.seckill.common.exceptions;

import com.lagou.seckill.common.dto.Result;
import com.lagou.seckill.common.enums.ErrorCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@ControllerAdvice
public class CustomExceptionHandler {

    @Value("${customException.apiReturnStack}")
    private boolean apiReturnStack;

    @SuppressWarnings("rawtypes")
    @ExceptionHandler(CustomException.ServiceException.class)
    public ResponseEntity handleExceptionOfServer(
            HttpServletRequest request,
            CustomException.ServiceException e) {
        String message = e.getMessage();
        log.error("CustomException.ServiceException", e);
        String code = e.getCode();
        Result result = new Result();
        result.setCode(code);
        result.setMsg(message);
        return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);

    }

    @SuppressWarnings("rawtypes")
    @ExceptionHandler(CustomException.ServiceExceptionSecond.class)
    public ResponseEntity handleExceptionOfServerSecond(
            HttpServletRequest request,
            CustomException.ServiceExceptionSecond e) {
        String message = e.getMessage();
        log.error("CustomException.ServiceExceptionSecond", e);
        String code = e.getCode();
        Result result = new Result();
        result.setCode(code);
        result.setMsg(message);
        return new ResponseEntity<>(result, HttpStatus.OK);

    }

//    @SuppressWarnings("rawtypes")
//    @ExceptionHandler(AuthorizationException.class)
//    public ResponseEntity handleExceptionOfAuth(
//            HttpServletRequest request,
//            AuthorizationException e) {
//        log.error("CustomException.AuthorizationException", e);
//
//        Result result = ResultFactory.wrapper(ErrorCodeEnum.SYSTEM_ERROR_B0403);
//        return new ResponseEntity<>(result, HttpStatus.FORBIDDEN);
//    }
//
//    @SuppressWarnings("rawtypes")
//    @ExceptionHandler(AuthenticationException.class)
//    public ResponseEntity handleExceptionOfAuth(
//            HttpServletRequest request,
//            AuthenticationException e) {
//        log.error("CustomException.AuthenticationException", e);
//
//        Result result = ResultFactory.wrapper(ErrorCodeEnum.SYSTEM_ERROR_B0401);
//        return new ResponseEntity<>(result, HttpStatus.UNAUTHORIZED);
//    }

    @SuppressWarnings("rawtypes")
    @ExceptionHandler(CustomException.class)
    public ResponseEntity<Result> handleExceptionOfClient(
            HttpServletRequest request, CustomException e) {

        log.error("CustomException", e);

        Result result = new Result();
        String code = ErrorCodeEnum.SYSTEM_ERROR_B0001.getCode();
        result.setCode(code);
        String message = e.getMessage();
        if(message != null){
            result.setMsg(message);
        }else{
            if(apiReturnStack){
                String stackTrace = ExceptionUtils.getStackTrace(e);
                result.setMsg(stackTrace);
            }else{
                result.setMsg(ErrorCodeEnum.SYSTEM_ERROR_B0001.getDescription());
            }
        }
        return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
    }


    @SuppressWarnings("rawtypes")
    @ExceptionHandler(RuntimeException.class)
    public ResponseEntity runtimeException(
            HttpServletRequest request, RuntimeException e) {
        log.error("RuntimeException", e);
        Result result = new Result();
        String code = ErrorCodeEnum.SYSTEM_ERROR_B0001.getCode();
        result.setCode(code);
        String message = e.getMessage();
        if(message != null){
            result.setMsg(message);
        }else{
            if(apiReturnStack){
                String stackTrace = ExceptionUtils.getStackTrace(e);
                result.setMsg(stackTrace);
            }else{
                result.setMsg(ErrorCodeEnum.SYSTEM_ERROR_B0001.getDescription());
            }
        }

        return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);


    }

    @SuppressWarnings("rawtypes")
    @ExceptionHandler(Exception.class)
    public ResponseEntity exceptionHandle(
            HttpServletRequest request, Exception e) {
        log.error("Exception", e);
        Result result = new Result();
        String code = ErrorCodeEnum.SYSTEM_ERROR_B0001.getCode();
        result.setCode(code);
        String message = e.getMessage();
        if(message != null){
            result.setMsg(message);
        }else{
            if(apiReturnStack){
                String stackTrace = ExceptionUtils.getStackTrace(e);
                result.setMsg(stackTrace);
            }else{
                result.setMsg(ErrorCodeEnum.SYSTEM_ERROR_B0001.getDescription());
            }
        }
        return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    /**
     * RequestBody请求参数校验错误.
     *
     * @param request 请求
     * @param e       异常
     * @return 响应
     */
    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public Object methodArgumentNotValidException(
            HttpServletRequest request, MethodArgumentNotValidException e) {
        log.error("MethodArgumentNotValidException", e);

        List<ArgumentInvalid> invalidArguments = new ArrayList<>();
		e.getBindingResult().getFieldErrors().forEach(
				fieldError -> invalidArguments.add(
						new ArgumentInvalid(fieldError.getField(),fieldError.getRejectedValue(),fieldError.getDefaultMessage())
						)
				);
        Result<List<ArgumentInvalid>> result = new Result<List<ArgumentInvalid>>();
        String code = ErrorCodeEnum.USER_ERROR_0001.getCode();
        result.setCode(code);
        result.setData(invalidArguments);
        String message = e.getBindingResult().getFieldErrors().get(0).getDefaultMessage();
        if (message != null) {
            	result.setMsg(message);
        } else {
            if (apiReturnStack) {
                String stackTrace = ExceptionUtils.getStackTrace(e);
                result.setMsg(stackTrace);
            } else {
                result.setMsg(ErrorCodeEnum.USER_ERROR_0001.getDescription());
            }
        }
        return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @EqualsAndHashCode(callSuper = false)
    static class ArgumentInvalid {
        private String field;
        private Object rejectedValue;
        private String defaultMessage;
    }
    


}
